home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacGofer 0.22d / MacGofer Sources / mac_io.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-08  |  21.1 KB  |  1,004 lines  |  [TEXT/MPS ]

  1. /*****************************************************************************
  2.  
  3.   mac_io.c:  Copyright (c) Kevin Hammond 1993.   All rights reserved.
  4.   
  5.   This module implements character-based IO, such as printf to the
  6.   worksheet, and keyboard input.  It handles i/o during evaluation
  7.   as well as input to the edit windows.
  8.  
  9.   IMPORTANT: DON'T INCLUDE the GOFER prelude.h.
  10.          This file uses the REAL fprintf etc.!!
  11.  
  12. *****************************************************************************/
  13.  
  14.  
  15. #include "mac.h"
  16. #include <stdarg.h>
  17. #include <string.h>
  18. #include <AppleEvents.h>
  19.  
  20. #pragma segment IO
  21.  
  22. extern Bool    USER_ABORT;        /* Abort Gofer                */
  23. extern Bool    terminalEchoReqd;    /* Whether to echo input        */
  24.  
  25. extern Boolean quit;            /* Set on application exit         */
  26. extern Boolean MemoryInstalledOK;    /* Whether initialisation's OK        */
  27. extern jmp_buf catch_error;        /* Standard Error Location        */
  28.  
  29. Boolean running_interpreter = FALSE;    /* Set if the interpreter's running    */
  30. Boolean HandlingEvents = FALSE;        /* Set if a graphics program's running    */
  31. Boolean EOFread = FALSE;        /* Set when EOF's been read        */
  32. static short inputafter = 0;        /* Start of user input                  */
  33.  
  34. Boolean cursorkey();            /* Test for a cursor key        */
  35. void CheckKeys();            /* Check whether a key's been pressed    */
  36. void DoKeyEvent();            /* Handle a key event             */
  37.  
  38. static short IntInterval = 0;
  39. #define IntThreshold 300
  40.  
  41.  
  42. /* Standard event masks for window events, and key events */
  43.  
  44. #define WinEventMask (updateMask|activMask|highLevelEventMask|osMask|mDownMask)
  45. #define KeyEventMask (keyDownMask|autoKeyMask|osMask|highLevelEventMask)
  46. #define HiPriEvt     (~(KeyEventMask|updateMask))
  47. #define    KEY_BUFFER_SIZE    256
  48. static EventRecord  KeyEvents[KEY_BUFFER_SIZE];
  49. static short firstkeyevent = 0, lastkeyevent = 0; 
  50.  
  51. /***************************************************************************
  52.  
  53.     Interrupt Handing.
  54.  
  55. ***************************************************************************/
  56.  
  57.  
  58. /*
  59.     Check for an interrupt (CMD-DOT) without losing keyboard events. 
  60. */
  61.  
  62.  
  63. CheckInterrupt()
  64. {
  65.   /* No interrupts at first */
  66.   if(!MemoryInstalledOK)
  67.     return;
  68.  
  69.   do {   
  70.     EventRecord myEvent, dummyEvent, *myEventP;
  71.   
  72.     int nextkeyevent = (lastkeyevent + 1) % KEY_BUFFER_SIZE;
  73.   
  74.     if(nextkeyevent == firstkeyevent)
  75.       myEventP = &myEvent;
  76.     else
  77.       myEventP = &(KeyEvents[lastkeyevent]);
  78.    
  79.     IntInterval = 0;
  80.  
  81.     (void) EventAvail(KeyEventMask,myEventP);
  82.  
  83.     /* Check for interrupt */
  84.     if(myEventP->what != nullEvent)
  85.       {
  86.         char  ch = (char) (myEventP->message & charCodeMask);
  87.     
  88.         GetNextEvent(KeyEventMask,&dummyEvent);
  89.  
  90.     if(myEventP->what == osEvt)
  91.       DoOSEvent();
  92.  
  93.     else if(myEventP->what == kHighLevelEvent)
  94.       DoHighLevelEvent();
  95.      
  96.         else 
  97.       {
  98.         if ((myEventP->modifiers & cmdKey) != 0 && ch == '.')
  99.               {
  100.               FlushEvents (everyEvent,0 );       /* Clear all outstanding events */
  101.                 lastkeyevent = firstkeyevent;         /* Including buffered characters */
  102.                 breakHandler();
  103.           }
  104.     
  105.             if ( nextkeyevent != firstkeyevent )
  106.               lastkeyevent = nextkeyevent;
  107.       }
  108.       }
  109.     else
  110.       break;
  111.  
  112.   } while (1);
  113. }
  114.  
  115.  
  116. /*
  117.     Handle any saved keyboard events.
  118.     Called after execution, or on initialisation.
  119. */
  120.  
  121.  
  122. FlushKeyEvents()
  123. {
  124.   while( firstkeyevent != lastkeyevent )
  125.     {
  126.       int savekeyevent = firstkeyevent;
  127.       firstkeyevent = (firstkeyevent + 1) % KEY_BUFFER_SIZE;
  128.       DoKeyEvent(&(KeyEvents[savekeyevent]),worksheet,FALSE);
  129.     }
  130. }
  131.  
  132.  
  133. GetNextKbdEvent(evtmask,checkonly)
  134. int evtmask, checkonly;
  135. {
  136.   /* First check for interrupts */
  137.   CheckInterrupt();
  138.   
  139.   if(firstkeyevent != lastkeyevent && (evtmask & KeyEventMask) != 0)
  140.     {
  141.       myEvent.what = nullEvent;
  142.       
  143.       if((evtmask & HiPriEvt) != 0 && EventAvail(evtmask & HiPriEvt,&myEvent) && !checkonly)
  144.         getnextevent(evtmask & HiPriEvt);
  145.      
  146.       if(myEvent.what == nullEvent)
  147.         {
  148.       myEvent.what =      KeyEvents[firstkeyevent].what;
  149.       myEvent.message =   KeyEvents[firstkeyevent].message;
  150.       myEvent.when =      KeyEvents[firstkeyevent].when;
  151.       myEvent.where =     KeyEvents[firstkeyevent].where;
  152.       myEvent.modifiers = KeyEvents[firstkeyevent].modifiers;
  153.  
  154.       if(!checkonly)
  155.             firstkeyevent = (firstkeyevent + 1) % KEY_BUFFER_SIZE;
  156.     }
  157.       
  158.     }
  159.   else
  160.    if(checkonly)
  161.      (void) EventAvail(evtmask,&myEvent);
  162.    else
  163.      getnextevent(evtmask);
  164. }
  165.  
  166. /***************************************************************************
  167.  
  168.     Output Handing.
  169.  
  170. ***************************************************************************/
  171.  
  172.  
  173. /*
  174.     Append a buffer of characters to the worksheet window.
  175. */
  176.  
  177. showbuff(buffer,buflen)
  178. char *buffer;
  179. long buflen;
  180. {
  181.   if (buflen > 0)
  182.     {
  183.       TEHandle teh = TEHANDLE(worksheet);
  184.  
  185.       if((*teh)->teLength + buflen > TE_REC_SIZE)
  186.         ClearSpace(teh,buflen);
  187.  
  188.       /* We add one to indicate the *right* of the last character */
  189.       TESetSelect((*teh)->teLength+1,(*teh)->teLength+1,teh);
  190.       TEInsert((Ptr)buffer,buflen,teh);
  191.       inputafter = (*teh)->selStart;
  192.     }
  193. }
  194.  
  195.  
  196.  
  197. /*
  198.     Append a single character to the worksheet window.
  199. */
  200.  
  201. showchar(c)
  202. char c;
  203. {
  204.   TEHandle teh = TEHANDLE(worksheet);
  205.   short caretState = (*teh)->caretState;
  206.   
  207.   if((*teh)->teLength == TE_REC_SIZE)
  208.     ClearLines(teh,100);
  209.   
  210. //  (*teh)->caretState = 0;
  211.   /* We add one to indicate the *right* of the last character */
  212.   TESetSelect((*teh)->teLength+1,(*teh)->teLength+1,teh);
  213.   TEInsert((Ptr)&c,1,teh);
  214.   inputafter = (*teh)->selStart;
  215. //  (*teh)->caretState = caretState;
  216. }
  217.  
  218.  
  219. /*
  220.     Make sure the worksheet scrolls to the right position during IO.
  221. */
  222.  
  223. updateWorksheet()
  224. {
  225.   AdjustScrollBars(worksheet);
  226.   ScrollToSelection(worksheet);
  227. }
  228.  
  229.  
  230.  
  231. /***************************************************************************
  232.  
  233.     Macintosh printf code.
  234.     
  235.     This code reimplements printf, so that characters can be
  236.     written correctly to the worksheet.  It has to be done this
  237.     way rather than using a single sprintf, because there is no version
  238.     of sprintf which takes an array of arguments in a form which
  239.     we can extract from the printf call.
  240.     
  241.     We also want to update the worksheet window whenever we see a
  242.     newline.
  243.  
  244. ***************************************************************************/
  245.  
  246.  
  247.  
  248. static char buffer[256];        /* Temporary buffer             */
  249. static char minifmt[20];        /* Holds a short format string         */
  250.  
  251. domprintf(fmt,argl)
  252. char *fmt;
  253. va_list *argl;
  254. {
  255.   va_list args = (va_list) argl;
  256.   char *buffp = buffer;
  257.  
  258.   int minisize;                /* -- used in a call to sprintf        */
  259.   Boolean found;            /* Set when a format has been found     */
  260.   char ch;
  261.   
  262.   minifmt[0]='%';
  263.   
  264.   while((ch = *fmt) != '\0')
  265.     {
  266.       if(ch == '%')
  267.         {
  268.       ++fmt;
  269.       for(found=FALSE, minisize = 1;
  270.           !found && (ch = *fmt) != '\0';
  271.           ++fmt)
  272.  
  273.             switch (ch)
  274.           {
  275.             case '%':
  276.               *buffp++ = '%';
  277.           found = TRUE;
  278.           break;
  279.  
  280. /*        All printfs of %c are ints rather than chars!
  281.  
  282.             case 'c':
  283.           strcpy(minifmt+minisize,"c");
  284.           sprintf(buffp,minifmt,va_arg(args,char));
  285.           buffp += strlen(buffp);
  286.           found = TRUE;
  287.               break;
  288. */
  289.  
  290.         case 'l':
  291.         case '.':
  292.         case '-': case '+':
  293.             case '1': case '2': case '3': case '4': case'5':
  294.             case '6': case '7': case '8': case '9': case'0':
  295.           minifmt[minisize++] = ch;
  296.           break;
  297.  
  298.         case 's': 
  299.           minifmt[minisize++]= ch;
  300.           minifmt[minisize] = '\0';
  301.           sprintf(buffp,minifmt,va_arg(args,char *));
  302.           buffp += strlen(buffp);
  303.           found = TRUE;
  304.               break;
  305.  
  306.         case 'f': case 'g':
  307.           minifmt[minisize++]= ch;
  308.           minifmt[minisize] = '\0';
  309.           sprintf(buffp,minifmt,va_arg(args,double));
  310.           buffp += strlen(buffp);
  311.           found = TRUE;
  312.               break;
  313.  
  314.         case 'c':            /* All Gofer chars are ints!  KH */        
  315.         case 'd': case 'x': case 'o':
  316.         case 'u': case 'i':
  317.         case 'X':
  318.           minifmt[minisize++]= ch;
  319.           minifmt[minisize] = '\0';
  320.           sprintf(buffp,minifmt,va_arg(args,int));
  321.           buffp += strlen(buffp);
  322.           found = TRUE;
  323.               break;
  324.  
  325.             default:
  326.           AbortErrorN("Unrecognised Control Char in printf: %%%c",(int)ch);
  327.           *buffp++ = ch;
  328.           }
  329.       }
  330.  
  331.       /* Flush the current buffer on newline */      
  332.       else if (ch == '\n' || ch == '\r' )
  333.         {
  334.           showbuff(buffer,(long)(buffp-buffer));
  335.               updateWorksheet();
  336.               showchar('\n');
  337.  
  338.           buffp=buffer; 
  339.           fmt++;
  340.         }
  341.  
  342.       else /* Neither % nor newline */
  343.         *buffp++ = *fmt++;
  344.     }
  345.   va_end(args);
  346.   showbuff(buffer,(long)(buffp-buffer));
  347.   updateWorksheet();
  348.   changed(worksheet);
  349. }
  350.  
  351.  
  352. /*
  353.     printf is aliased to this in the standard Gofer code.
  354. */
  355.  
  356. mprintf(fmt,argl)
  357. char *fmt;
  358. va_list argl;
  359. {
  360.   CheckInterrupt();
  361.   domprintf(fmt,&argl);
  362. }
  363.  
  364.  
  365. /*
  366.     fprintf is aliased to this in the standard Gofer code.
  367.     Send output to the worksheet for stdout/stderr, or to
  368.     a file for any other file pointer.
  369. */
  370.  
  371.  
  372. mfprintf(f,fmt,argl)
  373. FILE *f;
  374. char *fmt;
  375. va_list argl;
  376. {
  377.   CheckInterrupt();
  378.   
  379.   if(f == stdout || f == stderr)
  380.     domprintf(fmt,&argl);
  381.   else
  382.     fprintf(f,fmt,argl);
  383. }
  384.  
  385. /*
  386.     Output a character to either the worksheet or a file.
  387.     Implements the standard putc(char,file);
  388. */
  389.  
  390. mputc(c,f)
  391. FILE *f;
  392. int c;
  393. {  
  394.   if(f == stdout || f == stderr)
  395.     {
  396.       TEHandle teh = TEHANDLE(worksheet);
  397.  
  398.       showchar(c);
  399.       changed(worksheet);
  400.  
  401.       /* Moved out of loop to allow for auto-wrap on output */
  402.       if((*teh)->selStart == (*teh)->lineStarts[(*teh)->nLines])
  403.         {
  404.           AdjustScrollBars(worksheet);
  405.           ScrollToSelection(worksheet);
  406.     }
  407.  
  408.       if(c == '\r' || c == '\n')
  409.         CheckInterrupt();
  410.     }
  411.   else
  412.     {
  413.       fputc(c,f);
  414.       if(++IntInterval == IntThreshold)
  415.         CheckInterrupt();
  416.     }
  417. }
  418.  
  419.  
  420. /* Write a newline to the worksheet if not already at the start of a line */
  421.  
  422. writeNewLine()
  423. {
  424.   TEHandle teh = TEHANDLE(worksheet);
  425.   char *text;
  426.   char ch;
  427.   TESetSelect((*teh)->selEnd,(*teh)->selEnd,teh);
  428.   text = *(char **)((*teh)->hText);
  429.  
  430.   if( (ch=*(text+(*teh)->selStart-1)) != '\n' && ch != '\r' )
  431.     mputc('\n',stdout);
  432. }
  433.  
  434.  
  435.  
  436. /***************************************************************************
  437.  
  438.     Input Handing.
  439.  
  440. ***************************************************************************/
  441.  
  442.  
  443. #define KEY_SIZE 1024
  444. static char keybuff[KEY_SIZE];        /* The key input buffer        */
  445. static int  keystart = 0;        /* Head of queue        */
  446. static int  keyend = 0;            /* Tail of queue        */
  447.  
  448.  
  449. /*
  450.     Handle key presses as Toolbox Events.
  451. */
  452.  
  453.  
  454. static char tabs[] = "         ";
  455.  
  456. void DoKeyEvent(event,thewindow,domenus)
  457. EventRecord *event;
  458. int thewindow;
  459. Boolean domenus;
  460. {
  461.   char  ch = (char) (event->message & charCodeMask);                         /* Convert to ASCII */
  462.   int    nextkey;
  463.  
  464.   /* Handle command keys */      
  465.  
  466.   if ((event->modifiers & cmdKey) != 0)
  467.     {
  468.       if (ch == '.')
  469.         USER_ABORT = TRUE;
  470.  
  471.       else if (ch == ENTERkey)
  472.     EOFread = TRUE;
  473.  
  474.       /* The general-purpose code doesn't seem to trap CMD-? */
  475.       else if (ch == '?' || ch == '/')
  476.         {
  477.           DoMenuWindow(MItem_Help);
  478.       drawcursor(myEvent, FALSE);
  479.     }
  480.  
  481.       else if (domenus)
  482.         {
  483.           long result = MenuKey(ch);
  484.           DoMenu(result,event->modifiers);
  485.        }
  486.      }
  487.  
  488.    /* Abort if Escape/Clear pressed */      
  489.    else if (ch == ESC)
  490.      USER_ABORT = TRUE;
  491.  
  492.    /* Otherwise it's a normal character */
  493.    else if (isEditWindow(thewindow))
  494.      { 
  495.        ObscureCursor();
  496.        
  497.        /* If echo is off, don't echo to the worksheet */
  498.        if(!terminalEchoReqd && thewindow == worksheet)
  499.       {
  500.         if((nextkey=(keyend+1)%KEY_SIZE)!= keystart)
  501.           {
  502.             keybuff[keystart]=ch;
  503.         keyend = nextkey;
  504.           }
  505.         else
  506.           StopAlert(Res_Kbd_Buffer_Full_Alert,NIL);
  507.       }
  508.     else
  509.       {
  510.         TEHandle teh = TEHANDLE(thewindow);
  511.         
  512.         /* teLength removed to try to avoid crashing */
  513.         short selStart = (*teh)->selStart, selEnd = (*teh)->selEnd;
  514.         
  515.     
  516.         /* Set the changed flag, unless this was a cursor key */
  517.             if (!cursorkey(ch))
  518.           {
  519.             keyundo(thewindow);
  520.                 changed(thewindow);
  521.           }
  522.         else
  523.           cantundo();
  524.  
  525.         /* Return, newline and enter are treated uniformly */
  526.         if(ch=='\r' || ch == ENTERkey)
  527.           ch = '\n';
  528.  
  529.         if(ch=='\n' && thewindow == worksheet)
  530.           {
  531.             if(selEnd == (*teh)->teLength)
  532.           {
  533.             if((*teh)->teLength >= TE_REC_SIZE)
  534.               ClearSpace(teh,1000);
  535.  
  536.                     AdjustPromptCount(TRUE);              
  537.                     TEKey(ch,teh);
  538.           }
  539.             AdjustScrollBars(worksheet);
  540.             ScrollToSelection(worksheet);
  541.             ReadWorksheetLine();
  542.           }
  543.  
  544.         else if (ch != DELETEkey || thewindow != worksheet ||
  545.              selStart != inputafter || selStart != selEnd )
  546.           {
  547.             WindowKey(ch,thewindow);
  548.           }
  549.       }
  550.     }
  551.   AdjustMenus(FALSE);
  552. }
  553.  
  554.  
  555. /*
  556.     Handle a normal character key for a window.
  557. */
  558.  
  559. #define    TAB_SPACING    8
  560.  
  561. WindowKey(ch,thewindow)
  562. char ch;
  563. int thewindow;
  564. {
  565.   int charwidth;
  566.   TEHandle teh = TEHANDLE(thewindow);
  567.   short selStart = (*teh)->selStart,
  568.         selEnd =   (*teh)->selEnd;
  569.  
  570.   thefrontwindow = thewindow;
  571.   
  572.   /* Abort if the key can't be added to the window */
  573.   if(ch == TABkey && (*teh)->teLength >= TE_REC_SIZE-TAB_SPACING ||
  574.      (*teh)->teLength >= TE_REC_SIZE)
  575.     {
  576.       if(thewindow == worksheet)
  577.         ClearLines(teh,100);
  578.       else
  579.         AbortError("","Can't add any more text to this window");
  580.     }
  581.  
  582.   if(thewindow == worksheet && (selStart < inputafter || selEnd < inputafter) &&
  583.      !cursorkey(ch))
  584.     {
  585.       AdjustPromptCount(TRUE);
  586.     }
  587.  
  588.   /* Tabs are converted to an appropriate number of spaces on input */         
  589.   if(ch == TABkey)
  590.     {
  591.       int nl;
  592.       char ch1;
  593.       
  594.       for(nl=(*teh)->selStart-1; (ch1=*(*((*teh)->hText)+nl)) != '\n'&& ch1 != '\r'  && nl >= 0; --nl)
  595.         /* SKIP */;
  596.     
  597.       charwidth = TAB_SPACING-((*teh)->selStart-1-nl)%TAB_SPACING;
  598.       TEInsert((Ptr)tabs,(long)charwidth,teh);
  599.     }
  600.  
  601.   /* Other characters are just added to the window */
  602.   else
  603.     {
  604.       TEKey(ch,teh);
  605.       charwidth = ch == DELETEkey?-(selStart==selEnd):1;
  606.     }
  607.  
  608.   /* Update the characters after the prompt */
  609.   if(thewindow == worksheet && (selStart < inputafter || selEnd < inputafter) &&
  610.      !cursorkey(ch))
  611.     {
  612.       inputafter += charwidth;
  613.     }
  614.  
  615.   /* Fix up the scroll bars and adjust the window */         
  616.   AdjustScrollBars(thewindow);
  617.   ScrollToSelection(thewindow);
  618. }
  619.  
  620.  
  621. /* Remove the first nlines lines from the Text Edit record teh */
  622.  
  623. ClearLines(teh,nlines)
  624. TEHandle teh;
  625. int nlines;
  626. {
  627.   TESetSelect(0,(*teh)->lineStarts[nlines],teh);
  628.   TEDelete(teh);
  629.   TESetSelect((*teh)->teLength+1,(*teh)->teLength+1,teh);
  630. }
  631.  
  632. /* Clear some space at the start of the text edit record */
  633.  
  634. ClearSpace(teh,nchars)
  635. TEHandle teh;
  636. int nchars;
  637. {
  638.   TESetSelect(0,nchars,teh);
  639.   TEDelete(teh);
  640.   TESetSelect((*teh)->teLength+1,(*teh)->teLength+1,teh);
  641. }
  642.  
  643. /*
  644.     Works out where the prompt is on the current line, so that
  645.     this isn't counted when <CR> is pressed.
  646. */
  647.  
  648. static short selStart, selEnd;
  649.  
  650. AdjustPromptCount(delete)
  651. Boolean delete;
  652. {
  653.   TEHandle teh = TEHANDLE(worksheet);
  654.  
  655.   selEnd = (*teh)->selEnd;
  656.  
  657.   if(delete)
  658.       {
  659.         selStart = (*teh)->selStart;
  660.         if(selStart < inputafter)
  661.       {
  662.          int i;
  663.          
  664.              /* find line start corresponding to selStart */
  665.              for(i=1;(*teh)->lineStarts[i]<selStart;++i)
  666.                ;
  667.  
  668.             /* Allow for character at start of line */   
  669.             if(i < (*teh)->nLines && (*teh)->lineStarts[i]==selStart)
  670.               ++i;
  671.         
  672.             inputafter = (*teh)->lineStarts[i-1];
  673.       }
  674.       }
  675.     else if(selStart < inputafter)
  676.       {
  677.         if(selEnd < inputafter)
  678.           inputafter += selEnd - selStart;
  679.         else
  680.           inputafter = selEnd;
  681.       }
  682. }
  683.  
  684.  
  685. /*
  686.     Determines whether the TextEdit buffer contains a newline in
  687.     the specified selection region.
  688. */
  689.  
  690. Boolean findnl(teStart,teEnd,teh)
  691. short teStart,teEnd;
  692. TEHandle teh;
  693. {
  694.   char **hText = (char **) (*teh)->hText;
  695.   char ch;
  696.   for(;teStart<teEnd;teStart++)
  697.     if((ch=*((*hText)+teStart)) == '\n' || ch == '\r')
  698.       return(TRUE);
  699.   return(FALSE);
  700. }
  701.  
  702.  
  703. /*
  704.     Called when a selection is copied into the worksheet window,
  705.     it calls newinput, which will execute the interpreter if it's
  706.     not already running, whenever the input contains a newline.
  707. */
  708.  
  709.  
  710. PasteInput()
  711. {
  712.   if(selStart >= inputafter && findnl(selStart,selEnd,TEHANDLE(worksheet)))
  713.     newinput(selStart,selEnd,FALSE);
  714. }
  715.  
  716.  
  717. /*
  718.     Read a line from the worksheet.
  719. */
  720.  
  721. ReadWorksheetLine()
  722. {
  723.    short i;
  724.    short posn, teStart, teEnd;
  725.    TEHandle teh = TEHANDLE(worksheet);
  726.  
  727. retry:   
  728.    posn = (*teh)->selStart;
  729.    
  730.    /* find line start corresponding to posn */
  731.    for(i=1;(*teh)->lineStarts[i]<posn;++i)
  732.      ;
  733.  
  734.    /* Allow for character at start of line */   
  735.    if(i < (*teh)->nLines && (*teh)->lineStarts[i]==posn)
  736.      ++i;
  737.  
  738.    /* Calculate start and end points.  Allow for prompts */
  739.    teStart = (*teh)->lineStarts[i-1];
  740.    if(i == (*teh)->nLines && inputafter > teStart)
  741.      teStart = inputafter;
  742.    teEnd = i==(*teh)->nLines?(*teh)->teLength-1:(*teh)->lineStarts[i]-1;
  743.  
  744.    if((long)(*teh)->teLength + teEnd - teStart + 1 > TE_REC_SIZE)
  745.      {
  746.        ClearLines(teh,100);
  747.        goto retry;
  748.      }
  749.  
  750.    /*
  751.        Add the line to the end of the worksheet.
  752.        Include a NL except when we're at the end of the buffer.
  753.    */
  754.    
  755.    TESetSelect((**teh).teLength+1,(**teh).teLength+1,teh);
  756.  
  757.    if(i==(*teh)->nLines)
  758.      {
  759.        char ch = *(*(*teh)->hText + teEnd);
  760.        if (ch != '\n' && ch != '\r')
  761.           {
  762.            TEKey('\n',teh);
  763.         ++teEnd;
  764.       }
  765.      }
  766.    else
  767.      TEInsert((Ptr)((*(*teh)->hText)+teStart),teEnd-teStart+1,teh);
  768.  
  769.    updateWorksheet();
  770.  
  771.    newinput(teStart,teEnd,TRUE);
  772. }
  773.  
  774.  
  775. /*
  776.   Is ch a cursor key.
  777. */
  778.  
  779.  
  780. Boolean cursorkey(ch)
  781. char ch;
  782. {
  783.   return(ch == LARROWkey || ch == RARROWkey || ch == UARROWkey || ch == DARROWkey);
  784. }
  785.  
  786.  
  787. /*
  788.     Non-Multitasking input routine.
  789.     
  790.     Handle all outstanding key and window events during evaluation.
  791.     If we are handling our own events, then only intercept the
  792.     keyboard events.
  793.     
  794. */
  795.  
  796.  
  797.  
  798. void CheckKeys()
  799. {
  800.   EventRecord    event;
  801.   
  802.   int evtmask = HandlingEvents? KeyEventMask: (KeyEventMask|WinEventMask);
  803.   
  804.   FlushKeyEvents();
  805.  
  806.   while(EventAvail(evtmask,&event))
  807.     {
  808.       if(EventAvail(KeyEventMask,&event))
  809.         {
  810.           GetNextEvent(KeyEventMask,&event);
  811.           DoKeyEvent(&event,worksheet,FALSE);
  812.         }
  813.       else
  814.         {
  815.       /* Temporarily ignore project resources */
  816.       if(HandlingEvents)
  817.         useprojectresfile(FALSE,FALSE);
  818.  
  819.           eventloop(WinEventMask);
  820.  
  821.       /* Use project resources once more if handling events ourselves */      
  822.       if(HandlingEvents)
  823.         useprojectresfile(TRUE,FALSE);
  824.     }
  825.     }
  826. }
  827.  
  828.  
  829. /*
  830.     Reset the input buffer.
  831.     Should be called on initialisation or error.
  832. */
  833.  
  834. resetinput()
  835. {
  836.   keyend = keystart;
  837.   EOFread = FALSE;
  838.   terminalEchoReqd = TRUE;
  839.   inputafter = (*TEHANDLE(worksheet))->selStart;
  840. }
  841.  
  842.  
  843. /*
  844.     Get a character from the key buffer, or from a
  845.     file.
  846. */
  847.  
  848. int mgetc(stream)
  849. FILE *stream;
  850. {
  851.   int ch;
  852.  
  853.   if(stream == stdin)
  854.     {
  855.       while(keystart == keyend && !EOFread)
  856.     {
  857.       if(MultiTasking)
  858.         eventloop(everyEvent);
  859.       else
  860.         CheckKeys();
  861.       if(USER_ABORT)
  862.         {
  863.           USER_ABORT = FALSE;
  864.           breakHandler();
  865.         }
  866.       if(quit)
  867.         longjmp(catch_error,1);
  868.     }
  869.       
  870.       if(keystart==keyend && EOFread)
  871.         {
  872.           ch = EOF;
  873.       EOFread = FALSE;
  874.     }
  875.  
  876.       else
  877.         {
  878.           ch = keybuff[keystart];
  879.           keystart = (keystart + 1) % KEY_SIZE;
  880.     }
  881.     }
  882.   else
  883.     {
  884.       /* Check for CTRL-. even when reading files */
  885.       if(++IntInterval == IntThreshold)
  886.         CheckInterrupt();
  887.       ch = getc(stream);
  888.     }
  889.  
  890.   return(ch);
  891. }
  892.  
  893.  
  894. /*
  895.     "Steal" input from a textedit buffer.
  896. */
  897.  
  898. extern Boolean MemoryInstalledOK;
  899.  
  900. newinput(teStart,teEnd,appendNL)
  901. long teStart, teEnd;
  902. Boolean appendNL;
  903. {
  904.   char *text, lastch = ' ';
  905.   int nextkey;
  906.  
  907.   text = *(char **)(*TEHANDLE(worksheet))->hText;
  908.  
  909.   /* Try to protect the user from themselves */  
  910.   if(!MemoryInstalledOK)
  911.     {
  912.       SysBeep(3);
  913.       return;
  914.     }
  915.   
  916.   while(teStart <= teEnd && (nextkey=(keyend+1)%KEY_SIZE) != keystart)
  917.     {
  918.        /* Convert CR to NL */
  919.        if(text[teStart] == '\r')
  920.          text[teStart] = '\n';
  921.  
  922.        /* Convert intermediate newlines to spaces */    
  923.        if(teStart < teEnd && text[teStart] == '\n')
  924.          lastch = keybuff[keyend] = ' ';
  925.        else
  926.          lastch = keybuff[keyend] = text[teStart];
  927.  
  928.        ++teStart;
  929.        keyend = nextkey;
  930.     }
  931.  
  932.   if(appendNL && (nextkey=(keyend+1)%KEY_SIZE) != keystart && lastch != '\n')
  933.     {
  934.       keybuff[keyend] = '\n';
  935.       keyend = nextkey;
  936.     }
  937.  
  938.   /* Evaluate if we're not already doing so */
  939.   if(!running_interpreter)
  940.     {
  941.       running_interpreter = TRUE;
  942.       USER_ABORT = EOFread = FALSE;
  943.       HandlingEvents = FALSE;
  944.  
  945.       SpinCursor(0);
  946.       ShowCursor();
  947.       if(!MultiTasking)
  948.         disablemenus(TRUE);
  949.  
  950. /*      do
  951.         {
  952. */
  953.       EOFread = FALSE;
  954.           interpreter(0);
  955. /*
  956.     }
  957.       while(keystart != keyend);
  958. */
  959.  
  960.       /* Unload the project resources, if necessary */
  961.       useprojectresfile(FALSE,TRUE);
  962.  
  963.       DoneInterpreter();
  964.     }
  965. }
  966.  
  967.  
  968.  
  969. /*
  970.     Tidy up after running the interpreter.
  971. */
  972.  
  973. DoneInterpreter()
  974. {
  975.   thefrontwindow = findMyWindow(FrontWindow());
  976.   
  977.   running_interpreter = FALSE;
  978.        
  979.   disablemenus(FALSE);
  980.       
  981.   AdjustMenus(TRUE);
  982.   
  983.   resetinput();
  984.   
  985.   FlushKeyEvents();
  986.  
  987.   ResetCursor();          /* Reset the cursor from a watch cursor */
  988. }
  989.  
  990.  
  991. /*
  992.     Flush a file.  Don't do anything for the worksheet,
  993.     it's always "unbuffered".
  994. */
  995.  
  996. mflush(f)
  997. FILE *f;
  998. {
  999.   if(f == stdout || f == stderr)
  1000.     /* SKIP */;
  1001.   else
  1002.     fflush(f);
  1003. }
  1004.